
package DB;

import Data.*;

import java.sql.PreparedStatement;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

/**
 *Mapper Klasse, die Airport Objekte auf eine relationale DB abbildet.
 * Diese Klasse erlaubt zahlreiche Operationen, um einen Airport zu verwalten, z.B. eine LöschOperation
 * @author Stefan Fellner
 */
public class AirportMapper {

    /*
     * Um sicher zu stellen, dass nur eine Instanz von der Klasse erzeugt wird, wird die Singleton Pattern angewendet.
     * Durch das Schlüsselwort static wird dies erreicht.
     * Quelle: http://www.theserverside.de/singleton-pattern-in-java/
     */
    private static AirportMapper airportMapper = null;

    private ConnectionManager manager;

    //mittels protected wird sichergestellt, dass mit new keine neuen Instanzen erstellt werden können. Sobals ein Mapper Objekt erstellt wird, wird eine connection aufgebaut
    protected AirportMapper(){
        manager = ConnectionManager.getInstance();

    }

    /*Eine Instanz dieser Klasse sollte immer mittels der folgenden Methode erstellt werden.
     * Dadurch wird sicher gestellt, dass nur eine Instanz von dieser Klasse vorhanden ist.
     * @return DAS <code>AirportMapper</code> Objekt
     * @see airportMapper
     */
    public static AirportMapper getInstance(){

        if(airportMapper == null){
            airportMapper = new AirportMapper();
        }

        return airportMapper;

    }


//Es folgen nun alle methoden, die die Tabelle airport verändern können, bzw. deren inhalte abrufen können


    /*Ein Airport in die Datenbank einfügen. Dabei ist zu beachten, dass die InternationaleID unique ist und somit nur einmal existieren darf.
     * @param das Objekt welches hinzugefügt werden soll
     * @return das eingefügte Objekt.
     * Wenn diese methode eine java.sql.SQLException schmeißt, ist der Primärschlüssel, d.h. die internationale Kennung schon vorhanden und es wird ein null Objekt zurückgegeben.
     * Dies muss dem user angezeigt werden! z.B. "Airport kann nicht eingefügt werden. Überprüfen sie die AirportId auf Duplicate"
     */
    public Airport insert (Airport a){
        //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();

        try{
            // Leeres SQL-Statement (JDBC) anlegen
            Statement stmt = con.createStatement();

            try{
            stmt.executeUpdate("INSERT INTO airport (internationalId, portName, portCity, portCountry) " +
                            "VALUES (" +
                            "'" + a.getInternationalId()+ "'" + ", " +
                            "'" + a.getPortName() + "'" + ", " +
                            "'" + a.getPortCity() + "'" + ", " +
                            "'" + a.getPortCountry() + "'" +  ");");
            }
            catch(RemoteException ex){
                ex.printStackTrace();
                return null;
            }

        }
        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
            
        }
        //dieses return ist eigentlich nicht notwendig, da sich am Objekt nichts verändert. Wenn die internationalId schon existiert, wird eine Exception geschmissen
        return a;
    }







    /*Einen Airport updaten, d.h. die Attribute des Airports aktualisieren
     * @param das Airport Objekt mit den neuen Attributen
     */
     public void update (Airport a){

         //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();

        try{
                //sql statement
            PreparedStatement stmt = con.prepareStatement("UPDATE airport " +
                    "SET portName=" + "'" + a.getPortName() + "'" +", " +
                    "portCity=" + "'" + a.getPortCity() + "'" + ", " +
                    "portCountry=" + "'" + a.getPortCountry() + "'" + " " +
                    "WHERE internationalId = " + "'" + a.getInternationalId() + "'");
            //dieser integer beinhaltet die anzahl der veränderten datensätze nach einer bestimmten UPDATE operation
            int anzahlVeraenderterDatensaetze = stmt.executeUpdate();
            }
            //falls der Zugriff auf die Klasse Airport scheitert
            catch(RemoteException ex){
                ex.printStackTrace();
            }
        
        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }


     /*Einen bestimmten Airport in der Datenbank löschen
      * @param den zu löschenden Airport
      */
     public void delete (Airport a){

        //DB-Verbindung vom Connection Manager holen
        Connection con = this.manager.getConnection();

        try {
            // Leeres SQL-Statement anlegen
            Statement stmt = con.createStatement();

            stmt.executeUpdate("DELETE FROM airport " +
                               "WHERE internationalId = " + "'" + a.getInternationalId() + "'");
        }
        //falls der Zugriff auf die Klasse Airport fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }

     /*Alle Airports, die in der Tabelle vorhanden sind werden zurückgegeben
      *
      *@return Vector mit allen Airports
      */
     public Vector<Airport> findAll(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Airport Objekte festhält
         Vector<Airport> allAirports = new Vector<Airport>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allAirportsQueryResult = stmt.executeQuery("SELECT * FROM airport "+"ORDER BY internationalId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Airport Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allAirportsQueryResult.next()){
                 //ein neues airport Objekt anlegen
                 Airport a = new AirportImpl(allAirportsQueryResult.getString("internationalId"),
                                            allAirportsQueryResult.getString("portName"),
                                            allAirportsQueryResult.getString("portCity"),
                                            allAirportsQueryResult.getString("portCountry"));
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allAirports.addElement(a);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen airports aus der Tabelle wird zurückgegeben
         return allAirports;
     }

     /*Einen bestimmten Airport abhängig vom Namen finden
      *
      * @param den Airport Namen als String
      * @return das gesuchte Airport Objekt
      */

     public Airport findByName(String portName){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredAirport = stmt.executeQuery("SELECT internationalId, portName, portCity, portCountry FROM airport" +
                                                           " WHERE portName = "+"'"+portName+"'");

             //es gibt nur einen Airport mit gleichem Namen. Es wird zunächst geschaut ob, ein Airport in der REsult Tabelle ist
             if(desiredAirport.next()){
                 //den ergebnisDatensatz in ein Objekt umwandeln
                 Airport a = new AirportImpl(desiredAirport.getString("internationalId"),desiredAirport.getString("portName"),desiredAirport.getString("portCity"),desiredAirport.getString("portCountry"));
                 return a;
             }

         }
         //falls der Zugriff auf die Klasse Airport fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //wenn keine Airport gefunden wurde und keine fehlermeldung ausgegeben wurde, gibt die methode null zurück.
         return null;
     }



     /*Einen bestimmten Airport abhängig von der internationalID finden, z.B. LAX(Los Angeles)
      *
      * @param die Airport ID als String
      * @return das gesuchte Airport Objekt
      */

     public Airport findByInternationalId(String internationalId){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredAirport = stmt.executeQuery("SELECT * FROM airport "+
                                                           "WHERE internationalId = "+"'"+internationalId+"'");

             //es gibt nur einen Airport mit gleicher ID. Es wird zunächst geschaut ob, ein Airport in der ResultSet "Tabelle" ist
             if(desiredAirport.next()){
                 //den ergebnisDatensatz in ein Objekt umwandeln
                 Airport a = new AirportImpl(desiredAirport.getString("internationalId"),desiredAirport.getString("portName"),desiredAirport.getString("portCity"),desiredAirport.getString("portCountry"));
                 return a;
             }

         }
         //falls der Zugriff auf die Klasse Airport fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //wenn keine Airport gefunden wurde und keine fehlermeldung ausgegeben wurde, gibt die methode null zurück.
         return null;
     }


}
